-- AutoOperationM
-- create by cheny3 Apr/26/2016
-- 迷宫外自动操作

module("AutoOperationM", package.seeall);

local AUTO_START = 1;
local AUTO_PAUSE = 2;
local AUTO_STOP  = 3;

-- 每一步执行时间
local moveTime = 2;

-- 暂停时间
local pauseTime;

-- 当前状态
local state = AUTO_STOP;

-- 策略配置
local tacticsCfg = {};
-- 界面子规则
local rulesCfg = {};

-- 模块初始化操作
function init()
    -- 子规则
    rulesCfg = LOAD_PATH("game/ui/module/auto_operation");

    -- 配置
    local auto_operation_CSV = get_auto_operation_CSV();
    for i = 1, #auto_operation_CSV do
        tacticsCfg[auto_operation_CSV[i].form_name] = auto_operation_CSV[i];
    end
    auto_operation_CSV = {};

    -- 注册事件
    registerEvent();
end

-- 注册事件
function registerEvent()
    EventMgr.removeAll("AutoOperationM");
    -- TODO
end

-- 获取策略模板
function getModule(ruleName)
    local rule = rulesCfg[ruleName];
    if rule == "TBL" then
        rulesCfg[ruleName] = LOAD_RUN("game/ui/module/auto_operation/" .. ruleName);
        return rulesCfg[ruleName];
    end

    return rule;
end

-- 执行自动操作策略
function executeTactics()
    if state ~= AUTO_START then
        return;
    end

    -- 如果是在地牢中
    if DungeonM.getState() == DUNGEON_GAMING then
        cclog("玩家处于地牢中，无法执行迷宫外自动操作！");
        return;
    end

    -- 定时器
    ScheduleM.createScheme("AutoOperationM", deamon, moveTime, true);
end

-- 定时回调策略
function deamon()
    if not isInAutoOperation() then
        ScheduleM.deleteScheme("AutoOperationM");
        return;
    end

    if not ME.isInGame then
        stop();
        return;
    end

    -- 执行操作
    move();
end

-- 查询
function query(formName, path)
    local config = tacticsCfg[formName];
    if not config then
        return nil;
    end

    -- 防止被修改了
    config = table.deepcopy(config);

    if path then
        return config[path];
    end

    return config;
end

-- 打乱排序
function randomArr(arr)
    -- 随机两两交换
    for index, value in pairs(arr) do
        local randomIndex = math.random(#arr);
        local temp = arr[index];
        arr[index] = arr[randomIndex];
        arr[randomIndex] = temp;
    end
    return arr;
end

-- 排序子规则
function sortRules(rules)
    -- 先随机排序
    -- TODO 也许可以按一定权重来排序？
    return randomArr(rules);
end

-- 获取当前打开的界面
function getCurForm()
    -- 当前打开的界面
    local form;
    local dialog = UIMgr.getCurrentScene():getCurrentDialog();
    if dialog then
        -- 有模态窗口返回模态窗口
        form = dialog;
    else
        -- 否则返回最新打开的窗口
        form = UIMgr.getCurrentScene():getCurrentForm();
    end
    return form;
end

-- 操作
function move()
    if pauseTime and pauseTime > os.time() then
        -- 暂停时间
        return;
    end

    -- 当前打开的界面
    local formName = getCurForm():getName();

    -- 获取界面的子规则
    local rules = query(formName, "rules");
    local paras = query(formName, "paras");

    -- 没有这个界面的相关配置
    if not rules or #rules <= 0 then
        cclog(string.format("当前没有%s自动操作子规则.", formName));
        -- 直接调用button_click
        local mod = getModule("button_click");
        if not mod.canTrigger(paras)
            or not mod.trigger(paras) then
            -- 失败了就调用关闭界面
            mod = getModule("close_form");
            mod.trigger(paras);
        end
        return;
    end

    -- 打乱子规则
    rules = sortRules(rules);

    -- 遍历打乱后的子规则
    for _, ruleName in pairs(rules) do
        -- 找一个可以触发的规则
        local mod = getModule(ruleName);
        if mod and mod.canTrigger(paras) and mod.trigger(paras) then
            return;
        end
    end

    cclog("当前没有可以执行的操作！！！");
end

-- 设置暂停时间
function setPauseTime(setTime)
    pauseTime = setTime;
end

-- 开始
function start()
    if state == AUTO_START then
        return;
    end

    -- 改变状态
    state = AUTO_START;

    executeTactics();
end

-- 暂停
function pause()
    if state ~= AUTO_START then
        return false;
    end

    -- 改变状态
    state = AUTO_PAUSE;
end

-- 结束
function stop()
    if state == AUTO_STOP then
        return;
    end

    -- 改变状态
    state = AUTO_STOP;
end

-- 继续
function continue()
    if state ~= AUTO_PAUSE then
        return false;
    end

    start();
end

-- 是否在自动操作中
function isInAutoOperation()
    return state == AUTO_START;
end

-- 获取当前状态
function getState()
    return state;
end

-- 载入
init();

---------------////--------------- 点击相关

-- 抛出触摸事件
function dispatchClickEvent(clickNode)
    -- 获取事件分发器
    local dispatcher = clickNode:getEventDispatcher();

    -- 开始/结束必须使用同一个Event。
    -- 而且必须先调用开始。
    local event = cc.EventTouch:new();
    local pos = cc.p(clickNode:getPosition());
    pos = getScenePosition(clickNode, pos);

    -- 屏幕坐标是从左上角节点开始的
    event:setTouchInfo(pos.x, DESIGN_HEIGHT - pos.y);
    -- 抛出触摸事件
    for i = 1, 2 do
        local eventTypes = {cc.EventCode.BEGAN, cc.EventCode.ENDED};
        local eventType = eventTypes[i];
        event:setEventCode(eventType);
        dispatcher:dispatchEvent(event);
    end
end

-- 随机点击按钮
function buttonClick()
    -- 当前打开的界面
    local uiForm = getCurForm();
    local formName = uiForm:getName();
    local list = {};
    depthTraversal(uiForm.node, {} ,list);

    if #list <= 0 then
        cclog(string.format("界面%s没有可以点击按钮！", formName));
        return false;
    end

    -- 打乱节点,获取一个
    list = randomArr(list);
    local clickNode = list[1];

    -- 事件
    dispatchClickEvent(clickNode);
    return true;
end

-- 随机点击
function randomClick()
    -- 当前打开的界面
    local uiForm = getCurForm();
    local formName = uiForm:getName();

    -- 获取界面是配置click_paras
    local paras = query(formName, "click_paras") or {};

    -- 获取界面可以点击的控件名
    local list = {};
    depthTraversal(uiForm.node, paras, list);

    if #list <= 0 then
        cclog(string.format("界面%s没有可以点击节点！", formName));
        return false;
    end

    -- 打乱节点,获取一个
    list = randomArr(list);
    local clickNode = list[1];

    -- 事件
    dispatchClickEvent(clickNode);
    return true;
end

-- 深度遍历获得可以点击的节点
function depthTraversal(node, paras, list)
    if paras["filter"] == "name" then
        -- 根据名字获取可控节点
        for _, childNode in pairs(node:getChildren()) do
            -- 递归
            depthTraversal(childNode, paras, list);
            local name = childNode:getName();
            if table.indexOf(paras["list"], name) >= 0
                and childNode:isVisible() then
                -- 可以点击的
                table.insert(list, childNode);
            end
        end
    elseif paras["filter"] == "type" then
        -- 根据类型获得可控节点
        for _, childNode in pairs(node:getChildren()) do
            -- 递归
            list = depthTraversal(childNode, paras, list);
            local nodeType = childNode:getDescription();
            if table.indexOf(paras["list"], nodeType) >= 0
                and childNode:isVisible() then
                -- 可以点击的
                table.insert(list, childNode);
            end
        end
    else
        -- 没有配置的，默认点击按钮
        for _, childNode in pairs(node:getChildren()) do
            -- 递归
            list = depthTraversal(childNode, paras, list);
            local nodeType = childNode:getDescription();
            if nodeType == "Button" and childNode:isVisible()
                and childNode:isTouchEnabled() then
                -- 可以点击的
                table.insert(list, childNode);
            end
        end
    end
    return list;
end

-- 模拟点击
function virtualClickPoint(x, y)
    local event = cc.EventTouch:new();
    local pos = cc.p(x, y);
    event:setTouchInfo(pos.x, pos.y);
    local dispatcher = cc.Director:getInstance():getEventDispatcher();
    for i = 1, 2 do
        local eventTypes = {cc.EventCode.BEGAN, cc.EventCode.ENDED};
        local eventType = eventTypes[i];
        event:setEventCode(eventType);
        dispatcher:dispatchEvent(event);
    end
end

-- 递归获得节点在当前界面上的坐标，即相对于左下角
function getScenePosition(node, pos)
    local parentNode = node:getParent();
    if parentNode and parentNode:getName() ~= "" then
        pos = cc.p(parentNode:convertToWorldSpace(pos));
        if string.sub(parentNode:getName(), 1, 2) ~= "UI" then
            pos = getScenePosition(parentNode, pos);
        end
    end
    return pos;
end

-- -- 抛出触摸移动事件
-- function dispatchMoveEvent(clickNode)
--     -- 获取事件分发器
--     -- local dispatcher = clickNode:getEventDispatcher();
--     local dispatcher = cc.Director:getInstance():getEventDispatcher();

--     -- 开始/结束必须使用同一个Event。
--     -- 而且必须先调用开始。
--     local event = cc.EventTouch:new();
--     --local pos = cc.p(clickNode:getPosition());
--     --pos = getScenePosition(clickNode, pos);
--     local pos = cc.p(320, 500);

--     -- 移动的距离
--     local moveDistance = AlignM.frameSize.width / 5;
--     -- 随机一个方向
--     local direction = iif(math.random(1, 2) == 1, 1, -1);

--     -- 屏幕坐标是从左上角节点开始的
--     event:setTouchInfo(pos.x, DESIGN_HEIGHT - pos.y);
--     -- 抛出移动事件
--     for i = 1, 4 do
--         local eventTypes = {cc.EventCode.BEGAN, cc.EventCode.MOVED, cc.EventCode.MOVED, cc.EventCode.ENDED};
--         local eventType = eventTypes[i];
--         if i == 3 then
--             event:setTouchInfo(pos.x + direction * moveDistance, DESIGN_HEIGHT - pos.y);
--         end

--         event:setEventCode(eventType);
--         dispatcher:dispatchEvent(event);
--     end
-- end


